/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_T4HSpace_CollisionHandler_h__
#define __dylab_T4HSpace_CollisionHandler_h__

//===========================================================================================
#include <dylab/simobject/ComModelStructure.h>
#include <dylab/simobject/tetravol/Tetrahedron.h>
#include <dylab/simulation/ComCollisionHandler.h>
#include <dylab/simulation/ComEventListener.h>
#include <dylab/simulation/Simulation.h>
#include <dylab/simulation/ComGfx.h>
#include <dylab/utils/HashGenerator.h>
#include <dylab/utils/String.h>
#include <dylab/utils/containers/ListContainer.h>

//===========================================================================================
namespace dylab {

    class T4HSpace_CollisionHandler 
        : public ComCollisionHandler 
    {
    public:
        static const String COMPONENT_NAME;        

    private:
        class MassPointInfo
        {
        private:
            MassPoint * mMassPoint;
            ComModelStructure * mModel;

        public:
            MassPointInfo(MassPoint * _massPoint, ComModelStructure * _model)
                : mMassPoint(_massPoint), mModel(_model)
                    { 
                        DYLAB_ASSERT(mMassPoint != NULL);
                        DYLAB_ASSERT(mModel != NULL);
                    }
            MassPoint * getMassPoint()
                    { return mMassPoint; }
            ComModelStructure * getModelStructure()
                    { return mModel; }
        };
        //-----------------------------------------------------------------------------

    private:
        /// list of mass points info hashed to the same value in the table
        typedef ListContainer<MassPointInfo> MassPointInfoList;
        //-----------------------------------------------------------------------------

    private:        
        class MassPointHashTableEntry
        {
        private:            
            uint32_t mTimeStamp;            
            MassPointInfoList mMassPointInfoList;
        public:
            MassPointHashTableEntry(uint32_t _timeStamp)
                : mTimeStamp(_timeStamp)
                    { }
            void reset(uint32_t _timeStamp)
                    { 
                        mTimeStamp = _timeStamp; 
                        mMassPointInfoList.removeAll();
                    }
            uint32_t getTimeStamp() const
                    { return mTimeStamp; }
            MassPointInfoList & infoList()
                    { return mMassPointInfoList; }
            const MassPointInfoList & infoList() const
                    { return mMassPointInfoList; }
        };
        //-----------------------------------------------------------------------------

    private:        
        class CollisionInfo
        {
        private:    
            ComModelStructure * mModel;
            MassPoint * mMassPoint;
            Tetrahedron * mTetrahedron;            
        public:
            CollisionInfo(MassPoint * _mp, Tetrahedron * _tetra,
                            ComModelStructure * _model)
                : mMassPoint(_mp), mTetrahedron(_tetra), mModel(_model)
                    { }
            ComModelStructure * getModelStructure()
                    { return mModel; }
            MassPoint * getMassPoint()
                    { return mMassPoint; } 
            Tetrahedron * getTetrahedron()
                    { return mTetrahedron; }
        };
        //-----------------------------------------------------------------------------

    private:
        /// list of collision pairs (mass point - tetrahedron)
        typedef ListContainer<CollisionInfo> CollisionInfoList;
        //-----------------------------------------------------------------------------

    private:
        /// size of the hash table
        enum { HASH_TABLE_SIZE = 9973 };

    public:
        class OneShotObjectPoolController
            : public ComEventListener
        {
        private:
            Simulation * mSim;
            String mPoolName, mSceneName;
        public:
            OneShotObjectPoolController(const String & _poolName, const String & _sceneName = ComGfx::DEFAULT_NAME)
                : ComEventListener("OneShotObjectPoolController"), mPoolName(_poolName), mSceneName(_sceneName)
                    { }
            virtual String getDescription() const { return getName(); }
            virtual void initialize(Simulation * _parentSimulation)                
                    { mSim = _parentSimulation; }
			virtual void loadFromPropertyTree(const PropertyTree & _pt)
					{ }
            virtual Simulation * getParentSimulation()
                    { return mSim; }
            virtual const Simulation * getParentSimulation() const
                    { return mSim; }
            virtual void processEvent(const GfxEvent & _gfxEvent)
                    { 
                        ComGfx::IScene::ScopedExLock(mSim->getGfx(), mSceneName)
                            ->getObjectPool(mPoolName)->deleteAllObjects();
                    }
        };
        //-----------------------------------------------------------------------------

    private:
        class ProcessingModelInfo
        {
        private:
            ComModelStructure * mModel;

        public:
            ProcessingModelInfo(ComModelStructure * _model)
                : mModel(_model)
                    {
                        DYLAB_ASSERT(mModel != NULL);
                    }
            ComModelStructure * getModelStructure()
                    { return mModel; }
        };
        //-----------------------------------------------------------------------------

    private:
        typedef ListContainer<ProcessingModelInfo> ProcessingModels;
        //-----------------------------------------------------------------------------

    private:
        /// pointer to the foreign parent simulation object
        Simulation * mParentSimulation;
        /// size of the discretized space grid cell
        uint32_t mGridCellSize;
        /// mass point hash table
        MassPointHashTableEntry * mMassPointHashTable[HASH_TABLE_SIZE];
        /// for generating timestamps
        uint32_t mIterationCounter;
        /// list of all collisions in current iteration
        CollisionInfoList mCollisions;
        /// flag enabling the visual debuging of collisions
        bool mDebugCollisionsEnabled;
        /// material name to be used for debugged collision objects
        String mDebugCollisionsMaterial;
        /// object pool controller deletes all objects after each rendered frame
        OneShotObjectPoolController::Instance * mObjPoolController;
        /// list of all models to be processed
        ProcessingModels mProcessingModels;

    public:

        //-----------------------------------------------------------------------------
        // constructor & destructor
        //-----------------------------------------------------------------------------        
        T4HSpace_CollisionHandler();        
        virtual ~T4HSpace_CollisionHandler();        

        //-----------------------------------------------------------------------------
        // inherited methods
        //-----------------------------------------------------------------------------
        
        virtual String getDescription() const;        
        virtual void initialize(Simulation * _parentSimulation);   
		virtual void loadFromPropertyTree(const PropertyTree & _pt);
        virtual Simulation * getParentSimulation();
        virtual const Simulation * getParentSimulation() const;
        virtual void handleCollisions();

        //-----------------------------------------------------------------------------
        // custom methods
        //-----------------------------------------------------------------------------        

        void setDebugCollisionsEnabled(bool _enabled)
                { mDebugCollisionsEnabled = _enabled; }
        bool isDebugCollisionsEnabled() const
                { return mDebugCollisionsEnabled; }

        void setDebugCollisionsMaterial(const String & _material)
                { mDebugCollisionsMaterial = _material; }

        void setGridCellSize(uint32_t _gridSize)
                { mGridCellSize = _gridSize; }        

        uint32_t getGridCellSize() const
                { return mGridCellSize; }        

        void computeGridCoords(const Vector3 & _p, int32_t & i, int32_t & j, int32_t & k)
                {
                    i = static_cast<int32_t>(floor(_p.x() / static_cast<real_t>(mGridCellSize)));
                    j = static_cast<int32_t>(floor(_p.y() / static_cast<real_t>(mGridCellSize)));
                    k = static_cast<int32_t>(floor(_p.z() / static_cast<real_t>(mGridCellSize))); 
                }            

        uint32_t hashGridCoords(int32_t i, int32_t j, int32_t k)
                { return HashGenerator<int32_t>().addValue(i * 73856093).addValue(j * 19349663).addValue(k * 83492791).getHash() % HASH_TABLE_SIZE; }
    };
}
#endif // __dylab_T4HSpace_CollisionHandler_h__
//===========================================================================================